/**
  * Author: Mei
  * Date: 2020-12-08 19:22
  * Description: 
  * 交替打印
  */
#include <thread>
#include <iostream>
#include <semaphore.h>
#include <mutex>
#include <condition_variable>
#include <functional>

using namespace std;
int flag = 1;

class FooBar1 {
private:
    int n;
    sem_t sem;
public:
    FooBar(int n) {
        this->n = n;
        sem_init(&sem, 0, 0);
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            if (flag == 1) {
                printFoo();
                sem_post(&sem);
                flag = 0;
            } else {
                sem_wait(&sem);
                printFoo();
                sem_post(&sem);
            }
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            sem_wait(&sem); //如果为0 会等待 直到信号大于0
            printBar();
            sem_post(&sem);
        }
    }
};

class FooBar2 {
private:
    int n;
    mutex m1, m2;

public:
    FooBar(int n) {
        this->n = n;
        m2.lock();
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            m1.lock();
            // printFoo() outputs "foo". Do not change or remove this line.
            printFoo();
            m2.unlock();
        }
    }

    void bar(function<void()> printBar) {

        for (int i = 0; i < n; i++) {
            m2.lock();
            // printBar() outputs "bar". Do not change or remove this line.
            printBar();
            m1.unlock();
        }
    }
};

class FooBar3 {
private:
    int n;
    mutex mu;
    condition_variable cond;
    int count = 0;
public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            unique_lock<mutex> lock(mu);
            if (count == 1) {
                cond.wait(lock);
            }
            printFoo();
            ++count;
            cond.notify_one();
            lock.unlock();
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            unique_lock<mutex> lock(mu);
            if (count == 0) {
                cond.wait(lock);
            }
            printBar();
            --count;
            cond.notify_one();
            lock.unlock();
        }
    }
};

class FooBar {
private:
    int n;

public:
    FooBar(int n) {
        this->n = n;
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            std::unique_lock<std::mutex> lk(Mu);//获取Mu锁
            v.wait(lk, [this]() { return count == 1; });//看v是否满足条件，（锁和变量）
            // printFoo() outputs "foo". Do not change or remove this line.
            printFoo();
            count++;//变量+1，=2，等于2时，foo会阻塞，bar的condition_variable 满足条件
            v.notify_one();//通知并唤醒阻塞在v2里面的线程
        }
    }

    void bar(function<void()> printBar) {
        //注释同上
        for (int i = 0; i < n; i++) {
            std::unique_lock<std::mutex> lk(Mu);
            v.wait(lk, [this]() { return count == 2; }); //返回false 才会阻塞当前线程
            // printBar() outputs "bar". Do not change or remove this line.
            printBar();
            count--;
            v.notify_one();
        }
    }

private:
    int count = 1;//条件变量
    std::condition_variable v;//条件变量对象
    std::mutex Mu;//定义一个锁
};

void printFoo() {
    cout << "foo";
}

void printBar() {
    cout << "bar";
}

int main() {
    FooBar foo(5);
    thread f{&FooBar::foo, &foo, printFoo};
    thread f2{&FooBar::bar, &foo, printBar};

    f.join();
    f2.join();

    return 0;
}
